1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#![allow(non_camel_case_types, non_snake_case)]

use crate::co;
use crate::decl::*;
use crate::kernel::ffi_types::*;
use crate::ole::privs::*;

/// [`IUnknown`](crate::IUnknown) virtual table, base to all COM virtual tables.
#[repr(C)]
pub struct IUnknownVT {
	pub QueryInterface: fn(COMPTR, PCVOID, *mut COMPTR) -> HRES,
	pub AddRef: fn(COMPTR) -> u32,
	pub Release: fn(COMPTR) -> u32,
}

com_interface! { IUnknown: "00000000-0000-0000-c000-000000000046";
	/// [`IUnknown`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown)
	/// COM interface over [`IUnknownVT`](crate::vt::IUnknownVT). It's the base to
	/// all COM interfaces.
	///
	/// The `clone` method calls
	/// [`AddRef`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref)
	/// internally.
	///
	/// Automatically calls
	/// [`Release`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release)
	/// when the object goes out of scope.
}

/// This trait is enabled with the `ole` feature, and provides methods for
/// [`IUnknown`](crate::IUnknown). It is the base trait for all COM traits.
///
/// Prefer importing this trait through the prelude:
///
/// ```no_run
/// use winsafe::prelude::*;
/// ```
///
/// Note that the [`IUnknownVT`](crate::vt::IUnknownVT) virtual table has two
/// other methods: `AddRef` and `Release`. While these methods are relevant in
/// C++, here they are abstracted away as it follows:
///
/// * `AddRef` – called along the `clone` method from the
/// [`Clone`](std::clone::Clone) trait;
///
/// * `Release` – called automatically by the [`Drop`](std::ops::Drop) trait, so
/// you don't need to worry about it.
pub trait ole_IUnknown: Clone {
	/// The COM interface ID.
	const IID: co::IID;

	/// Creates an object from a COM virtual table pointer.
	///
	/// This method can be used as an escape hatch to interoperate with other
	/// libraries.
	///
	/// # Panics
	///
	/// Panics if trying to create a custom COM implementation object.
	///
	/// # Safety
	///
	/// Be sure the pointer points to a properly allocated COM virtual table.
	#[must_use]
	unsafe fn from_ptr(p: *mut std::ffi::c_void) -> Self;

	/// Returns the pointer to the underlying COM virtual table.
	///
	/// This method can be used as an escape hatch to interoperate with other
	/// libraries.
	#[must_use]
	fn ptr(&self) -> *mut std::ffi::c_void;

	/// Returns a mutable reference do the underlying COM virtual table pointer.
	///
	/// This method can be used as an escape hatch to interoperate with other
	/// libraries.
	///
	/// # Panics
	///
	/// Panics if trying to modify a custom COM implementation object.
	///
	/// # Safety
	///
	/// Be sure the pointer being set points to a properly allocated COM virtual
	/// table.
	#[must_use]
	unsafe fn as_mut(&mut self) -> &mut *mut std::ffi::c_void;

	/// Creates an object from a null COM virtual table pointer.
	///
	/// # Safety
	///
	/// The pointer must be initialized before any call. Do not call methods on
	/// a null COM pointer.
	#[must_use]
	unsafe fn null() -> Self {
		Self::from_ptr(std::ptr::null_mut())
	}

	/// Returns the pointer to the underlying COM virtual table and sets the
	/// internal pointer to null, so that
	/// [`Release`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release)
	/// won't be called.
	///
	/// Be sure to release the pointer, otherwise, as the name of this method
	/// implies, you will cause a resource leak.
	#[must_use]
	fn leak(&mut self) -> *mut std::ffi::c_void {
		let p = self.ptr();
		unsafe { *self.as_mut() = std::ptr::null_mut(); }
		p
	}

	/// [`IUnknown::QueryInterface`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void))
	/// method.
	#[must_use]
	fn QueryInterface<T>(&self) -> HrResult<T>
		where T: ole_IUnknown,
	{
		let mut queried = unsafe { T::null() };
		ok_to_hrresult(
			unsafe {
				(vt::<IUnknownVT>(self).QueryInterface)(
					self.ptr(),
					&T::IID as *const _ as _,
					queried.as_mut(),
				)
			},
		).map(|_| queried)
	}
}